home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 001-100 / 001-025 / 022 / pemacs / display.c < prev    next >
C/C++ Source or Header  |  1995-03-17  |  22KB  |  864 lines

  1. /*
  2.  * The functions in this file handle redisplay. There are two halves, the
  3.  * ones that update the virtual display screen, and the ones that make the
  4.  * physical display screen the same as the virtual display screen. These
  5.  * functions use hints that are left in the windows by the commands.
  6.  *
  7.  * REVISION HISTORY:
  8.  *
  9.  * ?    Steve Wilhite, 1-Dec-85
  10.  *      - massive cleanup on code.
  11.  */
  12.  
  13. #include        <stdio.h>
  14. #include        "ed.h"
  15.  
  16. #define WFDEBUG 0                       /* Window flag debug. */
  17.  
  18. typedef struct  VIDEO {
  19.         short   v_flag;                 /* Flags */
  20.         char    v_text[1];              /* Screen data. */
  21. }       VIDEO;
  22.  
  23. #define VFCHG   0x0001                  /* Changed. */
  24.  
  25. int     sgarbf  = TRUE;                 /* TRUE if screen is garbage */
  26. int     mpresf  = FALSE;                /* TRUE if message in last line */
  27. int     vtrow   = 0;                    /* Row location of SW cursor */
  28. int     vtcol   = 0;                    /* Column location of SW cursor */
  29. int     ttrow   = HUGE;                 /* Row location of HW cursor */
  30. int     ttcol   = HUGE;                 /* Column location of HW cursor */
  31.  
  32. VIDEO   **vscreen;                      /* Virtual screen. */
  33. VIDEO   **pscreen;                      /* Physical screen. */
  34.  
  35. /*
  36.  * Initialize the data structures used by the display code. The edge vectors
  37.  * used to access the screens are set up. The operating system's terminal I/O
  38.  * channel is set up. All the other things get initialized at compile time.
  39.  * The original window has "WFCHG" set, so that it will get completely
  40.  * redrawn on the first call to "update".
  41.  */
  42. vtinit()
  43. {
  44.     register int i;
  45.     register VIDEO *vp;
  46.  
  47.     (*term.t_open)();
  48.     vscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
  49.  
  50.     if (vscreen == NULL)
  51.         exit(1);
  52.  
  53.     pscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
  54.  
  55.     if (pscreen == NULL)
  56.         exit(1);
  57.  
  58.     for (i = 0; i < term.t_nrow; ++i)
  59.         {
  60.         vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
  61.  
  62.         if (vp == NULL)
  63.             exit(1);
  64.  
  65.         vscreen[i] = vp;
  66.         vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
  67.  
  68.         if (vp == NULL)
  69.             exit(1);
  70.  
  71.         pscreen[i] = vp;
  72.         }
  73. }
  74.  
  75. /*
  76.  * Clean up the virtual terminal system, in anticipation for a return to the
  77.  * operating system. Move down to the last line and clear it out (the next
  78.  * system prompt will be written in the line). Shut down the channel to the
  79.  * terminal.
  80.  */
  81. vttidy()
  82. {
  83.     movecursor(term.t_nrow, 0);
  84.     (*term.t_eeol)();
  85.     (*term.t_close)();
  86. }
  87.  
  88. /*
  89.  * Set the virtual cursor to the specified row and column on the virtual
  90.  * screen. There is no checking for nonsense values; this might be a good
  91.  * idea during the early stages.
  92.  */
  93. vtmove(row, col)
  94. {
  95.     vtrow = row;
  96.     vtcol = col;
  97. }
  98.  
  99. /*
  100.  * Write a character to the virtual screen. The virtual row and column are
  101.  * updated. If the line is too long put a "$" in the last column. This routine
  102.  * only puts printing characters into the virtual terminal buffers. Only
  103.  * column overflow is checked.
  104.  */
  105. vtputc(c)
  106.     int c;
  107. {
  108.     register VIDEO      *vp;
  109.  
  110.     vp = vscreen[vtrow];
  111.  
  112.     if (vtcol >= term.t_ncol)
  113.     {
  114.         vp->v_text[term.t_ncol - 1] = '$';
  115.     ++vtcol;
  116.     }
  117.     else if (c == '\t')
  118.         {
  119.         do
  120.             {
  121.             vtputc(' ');
  122.             }
  123.         while ((vtcol&0x07) != 0);
  124.         }
  125.     else if (c < 0x20 || c == 0x7F)
  126.         {
  127.         vtputc('^');
  128.         vtputc(c ^ 0x40);
  129.         }
  130.     else
  131.         vp->v_text[vtcol++] = c;                
  132. }
  133.  
  134. /*
  135.  * Erase from the end of the software cursor to the end of the line on which
  136.  * the software cursor is located.
  137.  */
  138. vteeol()
  139. {
  140.     register VIDEO      *vp;
  141.  
  142.     vp = vscreen[vtrow];
  143.     while (vtcol < term.t_ncol)
  144.         vp->v_text[vtcol++] = ' ';
  145. }
  146.  
  147. /*
  148.  * Make sure that the display is right. This is a three part process. First,
  149.  * scan through all of the windows looking for dirty ones. Check the framing,
  150.  * and refresh the screen. Second, make sure that "currow" and "curcol" are
  151.  * correct for the current window. Third, make the virtual and physical
  152.  * screens the same.
  153.  */
  154. update()
  155. {
  156.     register LINE *lp;
  157.     register WINDOW *wp;
  158.     register VIDEO *vp1;
  159.     register VIDEO *vp2;
  160.     register int i;
  161.     register int j;
  162.     register int c;
  163.  
  164.     wp = wheadp;
  165.  
  166.     while (wp != NULL)
  167.         {
  168.         /* Look at any window with update flags set on. */
  169.  
  170.         if (wp->w_flag != 0)
  171.             {
  172.             /* If not force reframe, check the framing. */
  173.  
  174.             if ((wp->w_flag & WFFORCE) == 0)
  175.                 {
  176.                 lp = wp->w_linep;
  177.  
  178.                 for (i = 0; i < wp->w_ntrows; ++i)
  179.                     {
  180.                     if (lp == wp->w_dotp)
  181.                         goto out;
  182.  
  183.                     if (lp == wp->w_bufp->b_linep)
  184.                         break;
  185.  
  186.                     lp = lforw(lp);
  187.                     }
  188.                 }
  189.  
  190.             /* Not acceptable, better compute a new value for the line at the
  191.              * top of the window. Then set the "WFHARD" flag to force full
  192.              * redraw.
  193.              */
  194.             i = wp->w_force;
  195.  
  196.             if (i > 0)
  197.                 {
  198.                 --i;
  199.  
  200.                 if (i >= wp->w_ntrows)
  201.                   i = wp->w_ntrows-1;
  202.                 }
  203.             else if (i < 0)
  204.                 {
  205.                 i += wp->w_ntrows;
  206.  
  207.                 if (i < 0)
  208.                     i = 0;
  209.                 }
  210.             else
  211.                 i = wp->w_ntrows/2;
  212.  
  213.             lp = wp->w_dotp;
  214.  
  215.             while (i != 0 && lback(lp) != wp->w_bufp->b_linep)
  216.                 {
  217.                 --i;
  218.                 lp = lback(lp);
  219.                 }
  220.  
  221.             wp->w_linep = lp;
  222.             wp->w_flag |= WFHARD;       /* Force full. */
  223.  
  224. out:
  225.             /* Try to use reduced update. Mode line update has its own special
  226.              * flag. The fast update is used if the only thing to do is within
  227.              * the line editing.
  228.              */
  229.             lp = wp->w_linep;
  230.             i = wp->w_toprow;
  231.  
  232.             if ((wp->w_flag & ~WFMODE) == WFEDIT)
  233.                 {
  234.                 while (lp != wp->w_dotp)
  235.                     {
  236.                     ++i;
  237.                     lp = lforw(lp);
  238.                     }
  239.  
  240.                 vscreen[i]->v_flag |= VFCHG;
  241.                 vtmove(i, 0);
  242.  
  243.                 for (j = 0; j < llength(lp); ++j)
  244.                     vtputc(lgetc(lp, j));
  245.  
  246.                 vteeol();
  247.                 }
  248.              else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0)
  249.                 {
  250.                 while (i < wp->w_toprow+wp->w_ntrows)
  251.                     {
  252.                     vscreen[i]->v_flag |= VFCHG;
  253.                     vtmove(i, 0);
  254.  
  255.                     if (lp != wp->w_bufp->b_linep)
  256.                         {
  257.                         for (j = 0; j < llength(lp); ++j)
  258.                             vtputc(lgetc(lp, j));
  259.  
  260.                         lp = lforw(lp);
  261.                         }
  262.  
  263.                     vteeol();
  264.                     ++i;
  265.                     }
  266.                 }
  267. #if WFDEBUG
  268. #else
  269.             if ((wp->w_flag&WFMODE) != 0)
  270.                 modeline(wp);
  271.  
  272.             wp->w_flag  = 0;
  273.             wp->w_force = 0;
  274. #endif
  275.             }           
  276. #if WFDEBUG
  277.         modeline(wp);
  278.         wp->w_flag =  0;
  279.         wp->w_force = 0;
  280. #endif
  281.         wp = wp->w_wndp;
  282.         }
  283.  
  284.     /* Always recompute the row and column number of the hardware cursor. This
  285.      * is the only update for simple moves.
  286.      */
  287.     lp = curwp->w_linep;
  288.     currow = curwp->w_toprow;
  289.  
  290.     while (lp != curwp->w_dotp)
  291.         {
  292.         ++currow;
  293.         lp = lforw(lp);
  294.         }
  295.  
  296.     curcol = 0;
  297.     i = 0;
  298.  
  299.     while (i < curwp->w_doto)
  300.         {
  301.         c = lgetc(lp, i++);
  302.  
  303.         if (c == '\t')
  304.             curcol |= 0x07;
  305.         else if (c < 0x20 || c == 0x7F)
  306.             ++curcol;
  307.  
  308.         ++curcol;
  309.         }
  310.  
  311.     if (curcol >= term.t_ncol)          /* Long line. */
  312.         curcol = term.t_ncol-1;
  313.  
  314.     /* Special hacking if the screen is garbage. Clear the hardware screen,
  315.      * and update your copy to agree with it. Set all the virtual screen
  316.      * change bits, to force a full update.
  317.      */
  318.     if (sgarbf != FALSE)
  319.         {
  320.         for (i = 0; i < term.t_nrow; ++i)
  321.             {
  322.             vscreen[i]->v_flag |= VFCHG;
  323.             vp1 = pscreen[i];
  324.             for (j = 0; j < term.t_ncol; ++j)
  325.                 vp1->v_text[j] = ' ';
  326.             }
  327.  
  328.         movecursor(0, 0);               /* Erase the screen. */
  329.         (*term.t_eeop)();
  330.         sgarbf = FALSE;                 /* Erase-page clears */
  331.         mpresf = FALSE;                 /* the message area. */
  332.         }
  333.  
  334.     /* Make sure that the physical and virtual displays agree. Unlike before,
  335.      * the "updateline" code is only called with a line that has been updated
  336.      * for sure.
  337.      */
  338.     for (i = 0; i < term.t_nrow; ++i)
  339.         {
  340.         vp1 = vscreen[i];
  341.  
  342.         if ((vp1->v_flag&VFCHG) != 0)
  343.             {
  344.             vp1->v_flag &= ~VFCHG;
  345.             vp2 = pscreen[i];
  346.             updateline(i, &vp1->v_text[0], &vp2->v_text[0]);
  347.             }
  348.         }
  349.  
  350.     /* Finally, update the hardware cursor and flush out buffers. */
  351.  
  352.     movecursor(currow, curcol);
  353.     (*term.t_flush)();
  354. }
  355.  
  356. /*
  357.  * Update a single line. This does not know how to use insert or delete
  358.  * character sequences; we are using VT52 functionality. Update the physical
  359.  * row and column variables. It does try an exploit erase to end of line. The
  360.  * RAINBOW version of this routine uses fast video.
  361.  */
  362. updateline(row, vline, pline)
  363.     char vline[];
  364.     char pline[];
  365. {
  366. #if RAINBOW
  367.     register char *cp1;
  368.     register char *cp2;
  369.     register int nch;
  370.  
  371.     cp1 = &vline[0];                    /* Use fast video. */
  372.     cp2 = &pline[0];
  373.     putline(row+1, 1, cp1);
  374.     nch = term.t_ncol;
  375.  
  376.     do
  377.         {
  378.         *cp2 = *cp1;
  379.         ++cp2;
  380.         ++cp1;
  381.         }
  382.     while (--nch);
  383. #else
  384.     register char *cp1;
  385.     register char *cp2;
  386.     register char *cp3;
  387.     register char *cp4;
  388.     register char *cp5;
  389.     register int nbflag;
  390.  
  391.     cp1 = &vline[0];                    /* Compute left match.  */
  392.     cp2 = &pline[0];
  393.  
  394.     while (cp1!=&vline[term.t_ncol] && cp1[0]==cp2[0])
  395.         {
  396.         ++cp1;
  397.         ++cp2;
  398.         }
  399.  
  400.     /* This can still happen, even though we only call this routine on changed
  401.      * lines. A hard update is always done when a line splits, a massive
  402.      * change is done, or a buffer is displayed twice. This optimizes out most
  403.      * of the excess updating. A lot of computes are used, but these tend to
  404.      * be hard operations that do a lot of update, so I don't really care.
  405.      */
  406.     if (cp1 == &vline[term.t_ncol])             /* All equal. */
  407.         return;
  408.  
  409.     nbflag = FALSE;
  410.     cp3 = &vline[term.t_ncol];          /* Compute right match. */
  411.     cp4 = &pline[term.t_ncol];
  412.  
  413.     while (cp3[-1] == cp4[-1])
  414.         {
  415.         --cp3;
  416.         --cp4;
  417.         if (cp3[0] != ' ')              /* Note if any nonblank */
  418.             nbflag = TRUE;              /* in right match. */
  419.         }
  420.  
  421.     cp5 = cp3;
  422.  
  423.     if (nbflag == FALSE)                /* Erase to EOL ? */
  424.         {
  425.         while (cp5!=cp1 && cp5[-1]==' ')
  426.             --cp5;
  427.  
  428.         if (cp3-cp5 <= 3)               /* Use only if erase is */
  429.             cp5 = cp3;                  /* fewer characters. */
  430.         }
  431.  
  432.     movecursor(row, cp1-&vline[0]);     /* Go to start of line. */
  433.  
  434.     while (cp1 != cp5)                  /* Ordinary. */
  435.         {
  436.         (*term.t_putchar)(*cp1);
  437.         ++ttcol;
  438.         *cp2++ = *cp1++;
  439.         }
  440.  
  441.     if (cp5 != cp3)                     /* Erase. */
  442.         {
  443.         (*term.t_eeol)();
  444.         while (cp1 != cp3)
  445.             *cp2++ = *cp1++;
  446.         }
  447. #endif
  448. }
  449.  
  450. /*
  451.  * Redisplay the mode line for the window pointed to by the "wp". This is the
  452.  * only routine that has any idea of how the modeline is formatted. You can
  453.  * change the modeline format by hacking at this routine. Called by "update"
  454.  * any time there is a dirty window.
  455.  */
  456. modeline(wp)
  457.     WINDOW *wp;
  458. {
  459.     register char *cp;
  460.     register int c;
  461.     register int n;
  462.     register BUFFER *bp;
  463.  
  464.     n = wp->w_toprow+wp->w_ntrows;              /* Location. */
  465.     vscreen[n]->v_flag |= VFCHG;                /* Redraw next time. */
  466.     vtmove(n, 0);                               /* Seek to right line. */
  467.     vtputc('-');
  468.     bp = wp->w_bufp;
  469.  
  470.     if ((bp->b_flag&BFCHG) != 0)                /* "*" if changed. */
  471.         vtputc('*');
  472.     else
  473.         vtputc('-');
  474.  
  475.     n  = 2;
  476.     cp = " MicroEMACS -- ";                     /* Buffer name. */
  477.  
  478.     while ((c = *cp++) != 0)
  479.         {
  480.         vtputc(c);
  481.         ++n;
  482.         }
  483.  
  484.     cp = &bp->b_bname[0];
  485.  
  486.     while ((c = *cp++) != 0)
  487.         {
  488.         vtputc(c);
  489.         ++n;
  490.         }
  491.  
  492.     vtputc(' ');
  493.     ++n;
  494.  
  495.     if (bp->b_fname[0] != 0)            /* File name. */
  496.         {
  497.         cp = "-- File: ";
  498.  
  499.         while ((c = *cp++) != 0)
  500.             {
  501.             vtputc(c);
  502.             ++n;
  503.             }
  504.  
  505.         cp = &bp->b_fname[0];
  506.  
  507.         while ((c = *cp++) != 0)
  508.             {
  509.             vtputc(c);
  510.             ++n;
  511.             }
  512.  
  513.         vtputc(' ');
  514.         ++n;
  515.         }
  516.  
  517. #if WFDEBUG
  518.     vtputc('-');
  519.     vtputc((wp->w_flag&WFMODE)!=0  ? 'M' : '-');
  520.     vtputc((wp->w_flag&WFHARD)!=0  ? 'H' : '-');
  521.     vtputc((wp->w_flag&WFEDIT)!=0  ? 'E' : '-');
  522.     vtputc((wp->w_flag&WFMOVE)!=0  ? 'V' : '-');
  523.     vtputc((wp->w_flag&WFFORCE)!=0 ? 'F' : '-');
  524.     n += 6;
  525. #endif
  526.  
  527.     while (n < term.t_ncol)             /* Pad to full width. */
  528.         {
  529.         vtputc('-');
  530.         ++n;
  531.         }
  532. }
  533.  
  534. /*
  535.  * Send a command to the terminal to move the hardware cursor to row "row"
  536.  * and column "col". The row and column arguments are origin 0. Optimize out
  537.  * random calls. Update "ttrow" and "ttcol".
  538.  */
  539. movecursor(row, col)
  540.     {
  541.     if (row!=ttrow || col!=ttcol)
  542.         {
  543.         ttrow = row;
  544.         ttcol = col;
  545.         (*term.t_move)(row, col);
  546.         }
  547.     }
  548.  
  549. /*
  550.  * Erase the message line. This is a special routine because the message line
  551.  * is not considered to be part of the virtual screen. It always works
  552.  * immediately; the terminal buffer is flushed via a call to the flusher.
  553.  */
  554. mlerase()
  555.     {
  556.     movecursor(term.t_nrow, 0);
  557.     (*term.t_eeol)();
  558.     (*term.t_flush)();
  559.     mpresf = FALSE;
  560.     }
  561.  
  562. /*
  563.  * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
  564.  * ABORT. The ABORT status is returned if the user bumps out of the question
  565.  * with a ^G. Used any time a confirmation is required.
  566.  */
  567. mlyesno(prompt)
  568.     char *prompt;
  569.     {
  570.     register int s;
  571.     char buf[64];
  572.  
  573.     for (;;)
  574.         {
  575.         strcpy(buf, prompt);
  576.         strcat(buf, " [y/n]? ");
  577.         s = mlreply(buf, buf, sizeof(buf));
  578.  
  579.         if (s == ABORT)
  580.             return (ABORT);
  581.  
  582.         if (s != FALSE)
  583.             {
  584.             if (buf[0]=='y' || buf[0]=='Y')
  585.                 return (TRUE);
  586.  
  587.             if (buf[0]=='n' || buf[0]=='N')
  588.                 return (FALSE);
  589.             }
  590.         }
  591.     }
  592.  
  593. /*
  594.  * Write a prompt into the message line, then read back a response. Keep
  595.  * track of the physical position of the cursor. If we are in a keyboard
  596.  * macro throw the prompt away, and return the remembered response. This
  597.  * lets macros run at full speed. The reply is always terminated by a carriage
  598.  * return. Handle erase, kill, and abort keys.
  599.  */
  600. mlreply(prompt, buf, nbuf)
  601.     char *prompt;
  602.     char *buf;
  603.     {
  604.     register int cpos;
  605.     register int i;
  606.     register int c;
  607.  
  608.     cpos = 0;
  609.  
  610.     if (kbdmop != NULL)
  611.         {
  612.         while ((c = *kbdmop++) != '\0')
  613.             buf[cpos++] = c;
  614.  
  615.         buf[cpos] = 0;
  616.  
  617.         if (buf[0] == 0)
  618.             return (FALSE);
  619.  
  620.         return (TRUE);
  621.         }
  622.  
  623.     mlwrite(prompt);
  624.  
  625.     for (;;)
  626.         {
  627.         c = (*term.t_getchar)();
  628.  
  629.         switch (c)
  630.             {
  631.             case 0x0D:                  /* Return, end of line */
  632.                 buf[cpos++] = 0;
  633.  
  634.                 if (kbdmip != NULL)
  635.                     {
  636.                     if (kbdmip+cpos > &kbdm[NKBDM-3])
  637.                         {
  638.                         ctrlg(FALSE, 0);
  639.                         (*term.t_flush)();
  640.                         return (ABORT);
  641.                         }
  642.  
  643.                     for (i=0; i<cpos; ++i)
  644.                         *kbdmip++ = buf[i];
  645.                     }
  646.  
  647.                 (*term.t_putchar)('\r');
  648.                 ttcol = 0;
  649.                 (*term.t_flush)();
  650.  
  651.                 if (buf[0] == 0)
  652.                     return (FALSE);
  653.  
  654.                 return (TRUE);
  655.  
  656.             case 0x07:                  /* Bell, abort */
  657.                 (*term.t_putchar)('^');
  658.                 (*term.t_putchar)('G');
  659.                 ttcol += 2;
  660.                 ctrlg(FALSE, 0);
  661.                 (*term.t_flush)();
  662.                 return (ABORT);
  663.  
  664.             case 0x7F:                  /* Rubout, erase */
  665.             case 0x08:                  /* Backspace, erase */
  666.                 if (cpos != 0)
  667.                     {
  668.                     (*term.t_putchar)('\b');
  669.                     (*term.t_putchar)(' ');
  670.                     (*term.t_putchar)('\b');
  671.                     --ttcol;
  672.  
  673.                     if (buf[--cpos] < 0x20)
  674.                         {
  675.                         (*term.t_putchar)('\b');
  676.                         (*term.t_putchar)(' ');
  677.                         (*term.t_putchar)('\b');
  678.                         --ttcol;
  679.                         }
  680.  
  681.                     (*term.t_flush)();
  682.                     }
  683.  
  684.                 break;
  685.  
  686.             case 0x15:                  /* C-U, kill */
  687.                 while (cpos != 0)
  688.                     {
  689.                     (*term.t_putchar)('\b');
  690.                     (*term.t_putchar)(' ');
  691.                     (*term.t_putchar)('\b');
  692.                     --ttcol;
  693.  
  694.                     if (buf[--cpos] < 0x20)
  695.                         {
  696.                         (*term.t_putchar)('\b');
  697.                         (*term.t_putchar)(' ');
  698.                         (*term.t_putchar)('\b');
  699.                         --ttcol;
  700.                         }
  701.                     }
  702.  
  703.                 (*term.t_flush)();
  704.                 break;
  705.  
  706.             default:
  707.                 if (cpos < nbuf-1)
  708.                     {
  709.                     buf[cpos++] = c;
  710.  
  711.                     if (c < ' ')
  712.                         {
  713.                         (*term.t_putchar)('^');
  714.                         ++ttcol;
  715.                         c ^= 0x40;
  716.                         }
  717.  
  718.                     (*term.t_putchar)(c);
  719.                     ++ttcol;
  720.                     (*term.t_flush)();
  721.                     }
  722.             }
  723.         }
  724.     }
  725.  
  726. /*
  727.  * Write a message into the message line. Keep track of the physical cursor
  728.  * position. A small class of printf like format items is handled. Assumes the
  729.  * stack grows down; this assumption is made by the "++" in the argument scan
  730.  * loop. Set the "message line" flag TRUE.
  731.  */
  732. mlwrite(fmt, arg)
  733.     char *fmt;
  734.     {
  735.     register int c;
  736.     register char *ap;
  737.  
  738.     movecursor(term.t_nrow, 0);
  739.     ap = (char *) &arg;
  740.     while ((c = *fmt++) != 0) {
  741.         if (c != '%') {
  742.             (*term.t_putchar)(c);
  743.             ++ttcol;
  744.             }
  745.         else
  746.             {
  747.             c = *fmt++;
  748.             switch (c) {
  749.                 case 'd':
  750.                     mlputi(*(int *)ap, 10);
  751.                     ap += sizeof(int);
  752.                     break;
  753.  
  754.                 case 'o':
  755.                     mlputi(*(int *)ap,  8);
  756.                     ap += sizeof(int);
  757.                     break;
  758.  
  759.                 case 'x':
  760.                     mlputi(*(int *)ap, 16);
  761.                     ap += sizeof(int);
  762.                     break;
  763.  
  764.                 case 'D':
  765.                     mlputli(*(long *)ap, 10);
  766.                     ap += sizeof(long);
  767.                     break;
  768.  
  769.                 case 's':
  770.                     mlputs(*(char **)ap);
  771.                     ap += sizeof(char *);
  772.                     break;
  773.  
  774.                 default:
  775.                     (*term.t_putchar)(c);
  776.                     ++ttcol;
  777.                 }
  778.             }
  779.         }
  780.     (*term.t_eeol)();
  781.     (*term.t_flush)();
  782.     mpresf = TRUE;
  783.     }
  784.  
  785. /*
  786.  * Write out a string. Update the physical cursor position. This assumes that
  787.  * the characters in the string all have width "1"; if this is not the case
  788.  * things will get screwed up a little.
  789.  */
  790. mlputs(s)
  791.     char *s;
  792.     {
  793.     register int c;
  794.  
  795.     while ((c = *s++) != 0)
  796.         {
  797.         (*term.t_putchar)(c);
  798.         ++ttcol;
  799.         }
  800.     }
  801.  
  802. /*
  803.  * Write out an integer, in the specified radix. Update the physical cursor
  804.  * position. This will not handle any negative numbers; maybe it should.
  805.  */
  806. mlputi(i, r)
  807.     {
  808.     register int q;
  809.     static char hexdigits[] = "0123456789ABCDEF";
  810.  
  811.     if (i < 0)
  812.         {
  813.         i = -i;
  814.         (*term.t_putchar)('-');
  815.         }
  816.  
  817.     q = i/r;
  818.  
  819.     if (q != 0)
  820.         mlputi(q, r);
  821.  
  822.     (*term.t_putchar)(hexdigits[i%r]);
  823.     ++ttcol;
  824.     }
  825.  
  826. /*
  827.  * do the same except as a long integer.
  828.  */
  829. mlputli(l, r)
  830.     long l;
  831.     {
  832.     register long q;
  833.  
  834.     if (l < 0)
  835.         {
  836.         l = -l;
  837.         (*term.t_putchar)('-');
  838.         }
  839.  
  840.     q = l/r;
  841.  
  842.     if (q != 0)
  843.         mlputli(q, r);
  844.  
  845.     (*term.t_putchar)((int)(l%r)+'0');
  846.     ++ttcol;
  847.     }
  848.  
  849. #if RAINBOW
  850.  
  851. putline(row, col, buf)
  852.     int row, col;
  853.     char buf[];
  854.     {
  855.     int n;
  856.  
  857.     n = strlen(buf);
  858.     if (col + n - 1 > term.t_ncol)
  859.         n = term.t_ncol - col + 1;
  860.     Put_Data(row, col, n, buf);
  861.     }
  862. #endif
  863.  
  864.